#pragma once

namespace ja2{ namespace vfs
{
	//! Template class for iterators.
	template<typename T, typename Ref>
	class Iterator : public boost::iterator_facade<Iterator<T, Ref>, T, boost::incrementable_traversal_tag, Ref>
	{
		// Inner classes
	public:
		//! Implementation.
		class IImplementation : boost::noncopyable
		{
			// Typedefs
		protected:
			typedef std::unique_ptr<IImplementation> Ptr_t;

			// Operations
		public:
			//! Get value.
			virtual Ref value() const = 0;
			//! Move to next.
			virtual void increment() = 0;
			//! Compare with other.
			virtual bool equal(IImplementation const &Right) const = 0;
			//! Clone the instance.
			virtual Ptr_t clone() const = 0;

			// Construction/Destruction
		public:
			virtual ~IImplementation() = 0 {}
		};

		// Attributes
	private:
		//! Implementation.
		std::shared_ptr<IImplementation> m_pImpl;

		// Operations
	public:
		//! Increment.
		void increment();
		//! Equality operation.
		bool equal(Iterator<T, Ref> const &Right) const;
		//! Dereference.
		Ref dereference() const;
		
		// Operators
	public:
		Iterator<T, Ref>& operator=(Iterator<T, Ref> const &Right);

		// Construction/Destruction
	public:
		Iterator(IImplementation *pImpl = nullptr);
		
		// Friends
		friend class boost::iterator_core_access;
	};

	template<class T, typename Ref>
	Iterator<T, Ref>::Iterator(IImplementation* pImpl)
		: m_pImpl(pImpl)
	{
	}

	template<class T, typename Ref>
	Iterator<T, Ref>& Iterator<T, Ref>::operator=(Iterator<T, Ref> const &Right)
	{
		// Delete old implementation and add new
		m_pImpl.reset(Right.m_pImpl->clone());
		
		return *this;
	}

	template<class T, typename Ref>
	void Iterator<T, Ref>::increment()
	{
		m_pImpl->increment();
	}
	
	template<class T, typename Ref>
	bool Iterator<T, Ref>::equal(Iterator<T, Ref> const &Right) const
	{
		JA2_ASSERT(typeid(*m_pImpl) == typeid(*Right.m_pImpl));
		return m_pImpl->equal(*Right.m_pImpl);
	}
	
	template<class T, typename Ref>
	Ref Iterator<T, Ref>::dereference() const
	{
		return m_pImpl->value();
	}
} /*vfs*/} /*ja2*/
